package com.zwl.smartplatform.service.impl;

import com.zwl.smartplatform.cache.DefaultRoleCache;
import com.zwl.smartplatform.cache.RoleCache;
import com.zwl.smartplatform.events.UserLoginEvent;
import com.zwl.smartplatform.exceptions.AddDataException;
import com.zwl.smartplatform.exceptions.MyBaseException;
import com.zwl.smartplatform.exceptions.QueryDataException;
import com.zwl.smartplatform.exceptions.ResultCode;
import com.zwl.smartplatform.mapper.UserMapper;
import com.zwl.smartplatform.pojo.*;
import com.zwl.smartplatform.service.UserService;
import com.zwl.smartplatform.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;


@Service
@Transactional//事务控制
public class UserServiceImpl implements UserService {

    private UserMapper userMapper;

    private DefaultRoleCache defaultRoleCache;

    private RoleCache roleCache;

    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setDefaultRoleCache(DefaultRoleCache defaultRoleCache) {
        this.defaultRoleCache = defaultRoleCache;
    }

    @Autowired
    public void setRoleCache(RoleCache roleCache) {
        this.roleCache = roleCache;
    }

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    /*

    添加用户操作
     */
    @Override
    public void addUser(User user) {

//        if(StringUtils.hasText(user.getUsername())){//数据不能是null或者是空 包括空格 要求至少存在一个有效的字符
//        //返回 用户没有传递必须传递的数据是不是有问题 有问题我们抛异常 谁愿意处理谁处理 跟我业务没关系 因为我们不负责返回响应结果
//        //我们要是抛出异常 抛什么？随便一个异常吗？ 我们发现我们需要携带数据出去 所以必须使用自定义异常
//        throw new MyBaseException("穿传递的用户名是空的", ResultCode.USERNAME_NULL);
//        }

//        if (user.isEmpty(CheckType.ADD)){
//             //如果返回true 一定代表着数据没有传递
//            throw new MyBaseException("传递的用户名是空的", ResultCode.USERNAME_NULL);
//        }

        //断言 我断言这个user一定符合我们的要求 只要不符合我就扔异常
        Assert.isTrue(!user.isEmpty(CheckType.ADD),()->{
            throw new AddDataException("必须传递到数据不完整，请检查", ResultCode.USERNAME_NULL);
        });

        //第二步 判断数据是不是已存在
        //用户名允许重复吗
        //手机号允许重复吗
        //邮箱允许重复吗
        //等等
        //根据用户名或者手机号或者邮箱查询用户
        List currentUser = userMapper.selectByNameOrPhoneOrEmail(user.getUsername(), user.getPhone(), user.getEmail());
//上面到对象必须是空的 不为空说明数据已经存在

//        if(currentUser!=null){
//            throw new MyBaseException("该用户已存在",)
//        }
        Assert.isTrue(currentUser.size()==0,()->{
            throw new MyBaseException("该用户已存在",ResultCode.DATA_ALREADY_EXIST);
        });
        //处理密码 将密码改成MD5值
        String password= user.getPassword();//传递到密码
       // String passwordMD5 = DigestUtils.md5DigestAsHex(password.getBytes());//将指定到数据转成MD5值
         //如果简单粗暴用上面到方式直接计算md5 相同的密码总是会得到相同到值 那么玩意数据泄露 还是很容易反向得到密码
        //我们需要为自己袋盐
        //生成随机字符串作为盐
        //程序中没有真正到随机数 程序中到随机数是种子计算出来到
        //如果想得到一个尽量随机到内容 怎么办
        String salt = MD5Utils.getRandomString(15);//生成了一个长度为15 的盐
        //至于是拼接在什么位置 无所谓 只有一点要求 这边怎么拼的 后面再计算的时候还怎么拼
        String md5 = MD5Utils.getMd5(password, salt, 1024);
        user.setPassword(md5);
        user.setPwdSalt(salt);
        user.setCreateTime(new Date());
        user.setCreateBy(user.getUsername());
        //数据库是不支持高并发到 你高冷一堆吃错误进去 不占用数据内存吗
        //缓存 我们发现角色相关的数据书相对来说几乎不会发生变化 我们没有必要每次都查询数据库 我们可以提前做成缓存 保存好 再需要的时候直接从缓存中获取到即可
        //应该放那？ 缓存什么时候加载？（只要初始化） 经过分析 在我们需要数据之前缓存就要初始化完成 name什么时候需要？任何时候都可能所以最好到办法实在程序启动完成后就加载缓存
        //什么时候程序启动完成了 我们有监听器 可以在程序启动到时候收到通知 可以在里面初始化缓存
        //但是我们发现spring有个监听器 在程序启动到时候初始化了对象 我们能不能利用它去实现

        //查询了默认的角色id
        //问题 ，我们这个用户的默认的角色id 机会不会变化 也就是每次注册添加什么数据 结果总是一样的 有必要每次都查询数据库吗？
//        Long roleId = defaultRoleMapper.selectRoleIdByType("user");
//        //根据角色id查询到了角色的信息
//        //同里 这个角色变化的频率相对来说也很低
//        Role role = roleMapper.selectRoleById(roleId);
        DefaultRole defaultRole = defaultRoleCache.getValue("user");
        Role role = roleCache.getValue(defaultRole.getRoleId());
        //设置角色信息
        user.setRemark(role.getRemark());
        userMapper.addUser(user);
        //用户的角色信息还没有保存
    }

    @Override
    public User login(String username, String password) {
        //第一步 我们判断到标准是和用户到数据进行比较 如果一致就通过
        //查询数据库 用什么条件查询？  账号查询 因为账号有唯一索引 密码没有
        //我们要求必须传递账号和密码
        Assert.isTrue(StringUtils.hasText(username)&&StringUtils.hasText(password),()->{
            throw  new QueryDataException("账号或者密码错误",ResultCode.USERNAME_PASSWORD_ERROR);
        });
        //如果查询不到 是返回null还是返回一个对象但里面到属性都是null
        User user = userMapper.selectUserByUserName(username);
        //只要查询不到就一定是账号查询错误
        Assert.notNull(user,()-> {
           throw new QueryDataException( "账号或者密码错误", ResultCode.USERNAME_PASSWORD_ERROR);
        });
        //有结果就要比较密码
        //但是用户传递的密码是明文 数据库中是md5 直接比较不行
        //因为md5不可逆 所以我们只能选择把用户传递的密码再次转成md5值 然后进行比较 但是转换的时候需要盐
        //盐再用户数据中 我们需要获取 然后用一开始注册时候生成md5的操作重新操作一遍
        String pwdSalt = user.getPwdSalt();//盐
        String comMd5 = MD5Utils.getMd5(password, pwdSalt, 1024);//计算出用户现在传递出来的md5
        Assert.isTrue(comMd5.equalsIgnoreCase(user.getPassword()),()->{
            throw new QueryDataException("账号或者密码错误",ResultCode.USERNAME_PASSWORD_ERROR);
        });
        //说明用户传递的账号和密码是对的
        user.setPassword(null);
        user.setPwdSalt(null);
        //发送事件 更新用户到一些登录后到数据
      //  context.publishEvent(new UserLoginEvent());
        return user;
    }

    @Override
    public int updateUserLogin(User user) {
        //判断时间 不判断 应为这个数据不是用户传递到 这个使我们自己生成到
        userMapper.upDateUserLogin(user);
        return 0;
    }
}
